-
Notifications
You must be signed in to change notification settings - Fork 8.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resolver Light Theme And Kibana Integration #67859
Resolver Light Theme And Kibana Integration #67859
Conversation
Pinging @elastic/endpoint-app-team (Feature:Resolver) |
Pinging @elastic/endpoint-data-visibility-team (Team:Endpoint Data Visibility) |
type ResolverColorNames = | ||
| 'ok' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ 🆒 Nice job cleaning up this 🌈 🗑️ .
b18fb6b
to
af8b31d
Compare
Planning another walk through with @lindseypoli to confirm changes on Friday |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking nice. Put some questions and comments for ya
type DateFormat = Date | number | string; | ||
export const getRelativeTimeDifference = (startDate: DateFormat, endDate: DateFormat): string => { | ||
// Set the threshold above which pluralization takes place https://momentjs.com/docs/#/customization/relative-time-threshold/ | ||
moment.relativeTimeThreshold('s', 60); // Least number of seconds to be considered minute |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've never used moment before, so excuse me if this is a dumb question. does this mutate a global 'moment' reference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, not dumb, and you're right. I'm looking into the FormattedRelative
component as a general replacement for this now, but I may either end up writing my own function or just sticking with moment as it doesn't look like FormattedRelative
provides a simple way to retrieve elapsed time '3 seconds ago' => '3 seconds'. I can check with @lindseypoli & @marrasherrier, if they would be okay with that more relative phrasing, but that may be more confusing to the user.
import { i18n } from '@kbn/i18n'; | ||
|
||
type DateFormat = Date | number | string; | ||
export const getRelativeTimeDifference = (startDate: DateFormat, endDate: DateFormat): string => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this function slow? do we run it on every frame, or are we memoizing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memoized in the processNodePositionsAndEdgeLineSegments
selector which I believe only runs on the initial load of the process tree.
const [numStr] = elapsedTime.split(' '); | ||
const weekVal = Math.floor(parseInt(numStr, 10) / 7); | ||
if (weekVal > 0) { | ||
elapsedTime = `${weekVal} ${weekVal > 1 ? 'weeks' : ' week'}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this use i18n? different locales might wannt to reperenst this differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a search and found
kibana/x-pack/plugins/security_solution/public/common/components/formatted_date/index.tsx
Line 9 in c98cfda
import { FormattedRelative } from '@kbn/i18n/react'; |
Would that approach work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it should be using i18n. And thanks for that. I may be able to go with that, but it's gonna depend on how design feels about the subtle change in phrasing. It might actually be better for me to go with FormattedNumber
or FormattedPlural
for the way we have it designed at the moment
/** | ||
* An array of vectors2 forming an polyline. Used to connect process nodes in the graph. | ||
* A tuple of 2 vector2 points and optional EdgeLineMetadata forming a polyline. Used to connect process nodes in the graph and display relevant information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- why is edgelinemetadata optional?
- The edge line was represented as an array of vectors since it was just a geometric concept. Now that there is other data involved, how about a shape like this:
type EdgeLine = {
points: [Vector2, Vector2];
elapsedTime: number;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
I'm guessing you meant optional? If so, it's because midway lines don't have any of that information
-
see below comment
* Values shared between two vertices joined by an edge line. | ||
*/ | ||
export interface EdgeLineMetadata { | ||
elapsedTime?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If elaspedTime
requires formatting, it may end up requiring DOM elements and the like. For that reason, perhaps it should be calculated in the react code.
If you went down that path, here's some ideas:
Instead of storing the (formatted) elasped time in state (or even storing the points) you could store the processes that the edge line connects:
type EdgeLine = {
from: ResolverEvent;
to: ResolverEvent;
}
Now the EdgeLine just represents the edge in the mathy-graphy sense.
Then you could add selectors like:
export function pointsForEdge(edgeLine: EdgeLine) {
return [positionForNode(edgeLine.from), positionForNode(edgeLine.to) ]
}
export function relativeTimeForEdge(edgeLine: EdgeLine) {
// im assuming that eventTimestamp is ms since epoch, if not, that conversion would be needed.
return eventTimestamp(edgeLine.to) - eventTimestamp(edgeLine.from)
}
Then in the react code, you could get the points from poinntsForEdge
by passing the edge (object reference itself.)
For rendering the time, you could do something like
<FormattedRelative value={relativeTimeForEdge(edgeLine)} />
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense, and is definitely more extensible. I'll look into implementing it.
const isShowingDescriptionText = magFactorX >= 0.55; | ||
|
||
const nodeXOffsetValue = isZoomedOut | ||
? -0.143413 + (-magFactorX * (1 - magFactorX)) / 10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain these magic numbers, and this expression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just modified the initial numbers set on 254 and 255 to better align on the graph. The function is just another magFactor dependent scale to help keep the position of the node centered as you scaled down and the event drop downs are removed from view.
? -0.53684 + (-magFactorX * 0.2 * (1 - magFactorX)) / magFactorX | ||
: -0.53684; | ||
|
||
const actionsBaseYOffsetPct = 5; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can rename it and add a description. It sets the top % offset for the process buttons to keep them aligned as the resolver scales down
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily something you need to take on, but it might be good for use to try moving display logic into CSS where possible.
@@ -360,17 +396,17 @@ const ProcessEventDotComponents = React.memo( | |||
return []; | |||
} | |||
// If we have entries to show, map them into options to display in the selectable list | |||
return Object.entries(relatedStats).map((statsEntry) => { | |||
const displayName = getDisplayName(statsEntry[0]); | |||
return Object.entries(relatedStats).map(([name, value]) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that this is older code. Any idea why we're using Object.entries
here? We control the data structure (for the most part) so maybe we could change this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, but I can update it to just a single iteration. 👍
id={labelId} | ||
size="s" | ||
style={{ | ||
maxHeight: `${Math.min(26 + magFactorX * 3, 32)}px`, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you explain this expression?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keeping the max-height of the button at 32, but it allows the sides of the button to better line up with the edge of the processNode
@@ -123,6 +134,8 @@ const NodeSubMenuComponents = React.memo( | |||
[menuAction] | |||
); | |||
|
|||
const closePopover = () => setMenuOpen(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you should use useCallback
here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can throw it in one, but it may be counter productive since it really doesn't have any dependencies. There was this article I read a little while ago from Kent Dodds that goes into it a bit: https://kentcdodds.com/blog/usememo-and-usecallback
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add it to my reading list. Can you summarize here? I'll elaborate on my reasoning. Maybe it's wrong:
Any time this component renders, the closePopover
reference will be new. The EuiPopover
instance below is passed closePopover
. If the EuiPopover
component re-renders when its properties change, it will be guaranteed to rerender. By passing a memoized version of the closePopover
function, we can avoid forcing EuiPopover
to rerender.
If we force EuiPopover to rerender, then readers of this code will need to understand the performance complexity of EuiPopover
in order to understand the perf complexity of this function (not meaning the strict technical definitions of those terms here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I read the article. This section talks about the use case I'm going for: https://kentcdodds.com/blog/usememo-and-usecallback#reactmemo-and-friends
More on closePopover
:
In our particular case, EuiPopover
fails to use React.memo
or PureComponent
. It will rerender on every frame. For that reason, I think you're correct: there is no performance benefit to using useCallback
for closePopover
. I think you're justified in leaving it out.
That being said, I would argue that EuiPopover
should use PureComponent
or useMemo
. As it is, those components will rerender on every frame. Even the memory allocated for 'jsx' (e.g. just stuff like <div />
) is substantial at that scale.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also re: the article. While I agree w/ a lot of what is said there. The author suggests not to use React.memo
. I would argue the opposite. In fact I'd argue that React shouldn't have any other way to create components.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also re: the article. While I agree w/ a lot of what is said there. The author suggests not to use
React.memo
. I would argue the opposite. In fact I'd argue that React shouldn't have any other way to create components.
Completely agree with this
b79e0a3
to
ea384d8
Compare
|
||
import { DurationDetails, DurationTypes } from '../types'; | ||
|
||
export const getFriendlyElapsedTime = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would you mind adding a comment like
/**
* Returns something. Used somehow.
*/
and would you mind adding an explicit return type.
Also, can you explain the reasoning for accepting either numbers or strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
eventTimestamp
depending on endgame event or current event will return either a string or number.
export function eventTimestamp(event: ResolverEvent): string | undefined | number { |
<> | ||
<linearGradient | ||
id={PaintServerIds.terminatedProcessCube} | ||
x1="10.5206" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❔ just curious - why are we using different US offsets for the light mode gradients?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same thing, but just following the exported SVG I was provided 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a couple 🔴 s on here I'd like to see answered but I'm giving a provisional thumb anyway. Maybe make issues for them if you can't address before merging. Really great job making this stuff look good here.
> | ||
<FormattedMessage | ||
id="xpack.securitySolution.endpoint.resolver.elapsedTime" | ||
defaultMessage="{duration} {durationType}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so in order to translate, you'd need to use a select
here I guess? like:
"{durationType, select, day {días} weeks {semanas}}"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was my thought that FormattedMessage actually translates this internally from what I see here https://formatjs.io/docs/react-intl/components/#formattedmessage and in other parts of Kibana
edb5ff0
to
c9be18b
Compare
💚 Build SucceededHistory
To update your PR or re-run it, just comment with: |
# Conflicts: # x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts # x-pack/plugins/security_solution/public/resolver/view/index.tsx # x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx
# Conflicts: # x-pack/plugins/security_solution/public/resolver/store/data/selectors.ts # x-pack/plugins/security_solution/public/resolver/view/index.tsx # x-pack/plugins/security_solution/public/resolver/view/process_event_dot.tsx
* master: (91 commits) [Search][BUG] Call wrong search strategy recursively in async search (elastic#69116) [Observability] Create context container to enable Observability plugin registry function (elastic#68642) Rename space id for disabled index pattern test (elastic#68990) skip flaky suite (elastic#63339) Resolver Light Theme And Kibana Integration (elastic#67859) [kbn/dev-utils] expose public tooling_log module (elastic#68868) index pattern(s) take dependencies as object (elastic#69055) include ci-stats metrics in pr comment (elastic#68563) Bump webpack packages (elastic#68716) [Uptime] Fixed metric query broken because of missing mapping (elastic#68999) Added cloud as an optional dependency (elastic#69050) Fixed all external links (elastic#68614) [DOCS] Reorganizes doc nav to match new Kibana nav (elastic#69069) [Endpoint] Using the stats provided by the backend for resolver UI (elastic#68577) [DOCS] Removees 8.0 from Upgrade Assistant docs (elastic#69067) [ML] Fix cloud deployment ID check (elastic#68695) [DOCS] Move metrics app content to metrics monitoring guide (elastic#69033) Add ingest manager topic to docs (elastic#68980) [SECURITY SOLUTION] EMT-401: add policy data to metadata and fix tests (elastic#68582) [DOCS] Fixes POST request for saved objects (elastic#69036) ...
* master: (60 commits) Re-enable mistakenly skipped tests. (elastic#69123) [Search][BUG] Call wrong search strategy recursively in async search (elastic#69116) [Observability] Create context container to enable Observability plugin registry function (elastic#68642) Rename space id for disabled index pattern test (elastic#68990) skip flaky suite (elastic#63339) Resolver Light Theme And Kibana Integration (elastic#67859) [kbn/dev-utils] expose public tooling_log module (elastic#68868) index pattern(s) take dependencies as object (elastic#69055) include ci-stats metrics in pr comment (elastic#68563) Bump webpack packages (elastic#68716) [Uptime] Fixed metric query broken because of missing mapping (elastic#68999) Added cloud as an optional dependency (elastic#69050) Fixed all external links (elastic#68614) [DOCS] Reorganizes doc nav to match new Kibana nav (elastic#69069) [Endpoint] Using the stats provided by the backend for resolver UI (elastic#68577) [DOCS] Removees 8.0 from Upgrade Assistant docs (elastic#69067) [ML] Fix cloud deployment ID check (elastic#68695) [DOCS] Move metrics app content to metrics monitoring guide (elastic#69033) Add ingest manager topic to docs (elastic#68980) [SECURITY SOLUTION] EMT-401: add policy data to metadata and fix tests (elastic#68582) ...
Summary
This PR introduces the light theme resolver as well as the functionality that hooks into the kibana theming. All former resolver functionality is retained with the addition of elapsed time displayed between the process nodes (ranging from milliseconds to years, though the latter probably is unlikely to actually occur). All other changes are primarily aesthetic and are listed below.
All colors are aligned with the dark and light themes colors provided in eui here: (https://elastic.github.io/eui/#/guidelines/colors)
Event buttons as well as description text disappears as we zoom out to give a better sense of space within the resolver. A tooltip is provided for the process name buttons when the width shortens at the most zoomed out level.
Event action buttons are displayed at the default scale on resolver load
Nodes are centered on the edge line and action buttons retain a central position relative to the node.
Panning controls are aligned with the themes
Event detail dropdown now uses the eui popover
Process nodes now have a hover state
Process node active outline now aligns with the button color
1. Light theme using current v7 light theme colors (really only relevant to button color)
**2. Action buttons hidden
**3. Description text hidden
4. Light theme using new amsterdam light theme (once again relevant here to only button color)
**5. Dark theme
Checklist
Delete any items that are not applicable to this PR.
For maintainers